package com.wf.demo.info.service;

import com.wf.demo.info.dao.DeviceDAO;
import com.wf.demo.info.dao.ParseDAO;
import com.wf.demo.info.pojo.*;
import com.wf.demo.info.util.Page4Navigator;
import com.wf.demo.info.util.ParseUtil;
import com.wf.demo.info.util.Result;
import com.wf.demo.job.service.DataDeviceService;
import com.wf.demo.job.util.RedisUtil;
import com.wf.demo.sys.annotation.SysLog;
import com.wf.demo.sys.pojo.SysLogEntity;
import com.wf.demo.sys.service.SysLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Service
@Component
public class ParseService {
    @Autowired
    ParseDAO parseDAO;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    DeviceDAO deviceDAO;
    @Autowired
    DeviceService deviceService;
    @Autowired
    MatchService matchService;
    @Autowired
    ModelService modelService;
    @Autowired
    DataDeviceService dataDeviceService;
    @Autowired
    AlarmService alarmService;
    @Autowired
    SysLogService sysLogService;
    //list某机型下的parse表，带页面信息的parse page
    public Page4Navigator<Parse> list(int mid, int start, int size, int navigatePages){
        Model model = modelService.get(mid);
        Sort sort = new Sort(Sort.Direction.ASC, "id");
        Pageable pageable = new PageRequest(start, size, sort);
        Page pageFromJPA = parseDAO.findByModel(model,pageable);
        return new Page4Navigator<>(pageFromJPA,navigatePages);
    }
    //list某机型下的parse表，
    public List<Parse> list( int mid){
        Model model =modelService.get(mid);
        Sort sort = new Sort(Sort.Direction.ASC,"id");
        return parseDAO.findByModel(model,sort);
    }

    public void delete(int id){
        parseDAO.delete(id);
    }
    public void update(Parse bean){
        parseDAO.save(bean);
    }
    public void add(Parse bean){
        parseDAO.save(bean);
    }
    public Parse get(int id){
        return parseDAO.findOne(id);
    }
    public boolean isExist(String name,int id) {
        Model model=modelService.get(id);
        Parse parse=parseDAO.findByDataNameAndModel(name,model);
        return null!=parse;
    }
    public boolean isExist(Parse bean) {
        int id =bean.getId();
        String name =bean.getDataName();
        Parse parse =parseDAO.getOne(id);

        Parse parse1=parseDAO.findByDataNameAndModel(name,bean.getModel());
        //确实重复了
        if(null!=parse1 && !parse.getDataName().equals(bean.getDataName()))
        return true;
        else{
            return false;
        }
    }
    /**
     *功能描述   解析数据框架  处理失败或接受的信息不对则返回false
     * @author wf
     * @date 2019/11/23
     * @param tcpData
     * @return java.lang.String
     */
    public Result processing(String tcpData,String url) {

        tcpData =tcpData.replace(" ",""); //去掉字符串所有空格
        List<Integer> data=new ArrayList<Integer>();
        for (int i = 0; i < tcpData.length() - 1; i += 2) {
            // grab the hex in pairs
            String output = tcpData.substring(i, (i + 2));
            // convert hex to decimal十六进制转换成int
            data.add(Integer.parseInt(output, 16)) ;
        }
        //转换成Integer数组
        Integer dataArray[]=data.toArray(new Integer[]{});
        //Integer数组转为int数组
        int[] intData = new int[dataArray.length];
        for(int i = 0;i<dataArray.length;i++){
            intData [i] = dataArray[i];
        }
        /**************检验北斗报文的正确性，并且判断是否是北斗指挥机的反馈信息******************/
        //先判断北斗报文的开头是什么
        char[] chars=new char[5];
        for (int i=0;i<5;i++){
            chars[i]= (char) intData[i];
        }
        String prefixStr=String.valueOf(chars);
        if(  !(prefixStr.equals("$TXXX") ||prefixStr.equals("$FKXX"))) { //如果既不是通信信息也不是反馈信息
            return Result.error("该数据不是通信信息，也不是反馈信息，不做解析处理");
        }
        else if(prefixStr.equals("$FKXX")){
            return  processingFeedback(intData);  //若是反馈信息
        }
        //“CRC 标志”：“00H”表示用户机收到此次信息的 CRC 校验正确；“01H”表示不正确。
        if(dataArray[dataArray.length-2] != 0)
            return Result.error("北斗报文CRC检验码错误");
        /**************检验北斗报文的正确性，并且判断是否是北斗指挥机的反馈信息******************/


        //得到设备的北斗卡号  05 D6 56 /382550 ,机型1  北斗报文中第12个字节
        int beidouId = dataArray[11]* 65536+dataArray[12]*256+dataArray[13];
        System.out.println("该数据中北斗卡号是"+beidouId);
        //根据北斗卡号查找device
        Device device = deviceDAO.findByBeidouId((long)beidouId);//强制转换成long
        if(device == null){
            return Result.error("该北斗卡号不存在");
        }

        //设备是否处于报废状态,不再接收北斗数据，直接退出，同时也会变为离线状态。
        if(device.getStatus() == 0){
            device.setOnline(false);
            deviceDAO.save(device);
            System.out.print("设备已经报废，不再接收北斗数据，为离线状态");
            return Result.error("设备已经报废，不再接收北斗数据，为离线状态");
        }
        /*************保存北斗客户端的ip+port*************/
        redisUtil.set("beidouTcpClientIp+Port",url);


        /*************在线模块*************/
            deviceService.setOnlineAndTime(device);
        /*************完成在线设置*************/

        /*************服务时间设置*************/
        deviceService.setServiceTime(device);
        /*************完成服务时间设置*************/

        //device得到机型
        Model model=device.getModel();
        //model得到解析表集合
        List<Parse> parses= parseDAO.findByModel(model);
        /***********对传输内容的长度和parse表的长度进行校验**********/
        int maxLength= parses.get(parses.size()-1).getByteEnd(); //parse中最长的字节数
        int dataLenth= (intData[16]*256+intData[17])/8;  //信息-电文长度
        if(maxLength !=dataLenth){
            //长度不匹配则出错，退出解析
            return Result.error("长度不匹配则出错，退出解析");
        }
        /**
         * 判断异或校验码的正确性需要吗？
         * */
        //开始遍历parse表解析数据
        List<BigDecimal> dataValues =new ArrayList<BigDecimal>();  //数据解析结果的集合
        List<String> infoString= new ArrayList<>();
        List<Alarm> alarms =new ArrayList<>();      //解析报警信息的集合
        for (Parse parse: parses){
            BigDecimal dataValue=ParseUtil.getValueByParse(intData,parse);//解析数据
            /*************开始报警检查*************/
            Alarm alarm =alarmService.judgeAlarm(parse,dataValue,device);
            //单个数据生成的alarm不为空，即数据不正常,则放入集合中
            if( alarm.getAlarmType() != 2){ //2是经过检查无异常
                alarms.add(alarm);
            }
            /*************完成报警检查*************/
            dataValues.add(dataValue);//数值加入集合
            //判断是否需要调用匹配文字信息函数(matchService.getStringByMatch会自己根据parse判断)
            String str=matchService.getStringByMatch(parse,dataValue);
            if(! str.isEmpty())
            infoString.add(str);
        }
        /*************开始储存数据到data实体中*************/

        dataDeviceService.addData(dataValues, infoString,device);

        /*************完成储存*************/

        /********对得到的Alarm集合进行处理*********/
        //alarm集合为空，表示数据正常
       if (alarms.isEmpty()){
           if (device.getStatus() == 1){
               //设备状态本身为正常，不做任何处理
               System.out.println("alarm集合为空，数据正常");
               return Result.ok("alarm集合为空，数据正常");
           }
           else if (device.getStatus() == 2){
               //设备状态本身为报警,即报警状态的设备接收到了正常的数据，需要对ALarm写入endTime
               alarmService.setEndTime(device);
               System.out.println("报警设备得到正常数据，恢复正常");
               device.setStatus(1);
               deviceDAO.save(device);
               return Result.ok("报警设备得到正常数据，恢复正常");
           }
       }
       //数据不正常，需报警,报警前先看看设备的状态
        else {
            if (device.getStatus() == 2){
                //设备状态本身为报警，不做任何处理
            }
           else if (device.getStatus() == 1){
               //设备状态本身为正常，将设备状态置为报警，报警信息存储到数据库
                device.setStatus(2);
                deviceDAO.save(device);
                for (Alarm alarm: alarms)
                    alarmService.add(alarm);
           }
       }
        return Result.ok();
    }
    /**
     *功能描述  处理反馈信息
     * @author wf
     * @date 2020/7/15
     * @param fkxx
     * @return com.wf.demo.info.util.Result
     */

    public  Result processingFeedback(int[] fkxx) {
        SysLogEntity  sysLogEntity=new SysLogEntity();

        sysLogEntity.setUsername("beidou");

        sysLogEntity.setMethod("com.wf.demo.info.service.processingFeedback");
        sysLogEntity.setParams(String.valueOf(fkxx[10]));    //反馈信息中的标志位
        sysLogEntity.setTime(5l);
        sysLogEntity.setIp("0:0:0:0:0:0:0:1");
        //标志位是第十一个数字
        int tag=fkxx[10];
        if(tag==0){
            sysLogEntity.setMethod("反馈信息,发送成功");
            sysLogService.add(sysLogEntity);
            return Result.ok();
        }
        else {
            sysLogEntity.setMethod("反馈信息,发送失败");
            sysLogService.add(sysLogEntity);
            return Result.error(String.valueOf(tag));
        }
    }

    /**
     *功能描述  对新添加的机型进行初始化配置
     * @author wf
     * @date 2019/12/8
     * @return
     */
    public void addModelInit(Model model){
        //对机型的parse初始化
        List<Parse>  parses=new ArrayList<>();
        Parse parse1=new Parse( model,"经度","longitude","float",2,
                5,false,false,new BigDecimal(-180),new BigDecimal(180));
        Parse parse2=new Parse( model,"纬度","latitude","float",6,
                9,false,false,new BigDecimal(-90) ,new BigDecimal(90) );
        Parse parse3=new Parse(model,"转速","rotateSpeed","float",10,
                13,false,false,new BigDecimal(0) ,new BigDecimal(3000));
        Parse parse4=new Parse(model,"运行状态","status","unsignedShort",14,
                15,false,true,null ,null);
        Parse parse5=new Parse(model,"滑油压力","greasePressure","float",16,
                19,false,false,new BigDecimal(0) ,new BigDecimal(1000));
        Parse parse6=new Parse(model,"冷却水温度","WaterTemperature","float",20,
                23,false,false,new BigDecimal(-20) ,new BigDecimal(120));
        Parse parse7=new Parse(model,"故障码","alarmCode","unsignedShort",24,
                25,false,true,null ,null);
        parses.add(parse1);
        parses.add(parse2);
        parses.add(parse3);
        parses.add(parse4);
        parses.add(parse5);
        parses.add(parse6);
        parses.add(parse7);
        for(Parse parse:parses){
            parseDAO.save(parse);
        }
    }
    /**
     *功能描述   5G解析数据框架  处理失败或接受的信息不对则返回false
     * @author wf
     * @date 2019/11/23
     * @param tcpData
     * @return java.lang.String
     */
    public Result processing5G(String tcpData,String url) {

            tcpData =tcpData.replace(" ",""); //去掉字符串所有空格
            List<Integer> data=new ArrayList<Integer>();
            for (int i = 0; i < tcpData.length() - 1; i += 2) {
                // grab the hex in pairs
                String output = tcpData.substring(i, (i + 2));
                // convert hex to decimal十六进制转换成int
                data.add(Integer.parseInt(output, 16)) ;
            }
            //转换成Integer数组
            Integer dataArray[]=data.toArray(new Integer[]{});
            //Integer数组转为int数组
            int[] intData = new int[dataArray.length];
            for(int i = 0;i<dataArray.length;i++){
                intData [i] = dataArray[i];
            }
            //创建一个长度78字节的数组，按照北斗的数据位置将5G数据放进去，5G的有效数据是从第六个字节开始的，对应北斗的第20个
            //将有效数据全部复制进去
            int[] data5gToBeidou=new int[78];
            for(int i = 0;i<dataArray.length-5;i++){
                data5gToBeidou [i+19] = dataArray[i+5];
            }

            //得到设备的控制系统编号
            int csNumber = dataArray[1]* 65536*256+dataArray[2]* 65536+dataArray[3]*256+dataArray[4];
            System.out.println("该数据中设备的控制系统编号是"+csNumber);
            //根据控制系统编号查找device
            Device device = deviceDAO.findByCsNumber(Integer.toString(csNumber));
            if(device == null){
                return Result.error("该控制系统编号不存在");
            }
            /**保存5G模块的ip+port**/
                device.setIpPort(url);
            /**************************/
            //设备是否处于报废状态,不再接收北斗数据，直接退出，同时也会变为离线状态。
            if(device.getStatus() == 0){
                device.setOnline(false);
                deviceDAO.save(device);
                System.out.print("设备已经报废，不再接收北斗数据，为离线状态");
                return Result.error("设备已经报废，不再接收北斗数据，为离线状态");
            }

            /*************在线模块*************/
            deviceService.setOnlineAndTime(device);
            /*************完成在线设置*************/

            /*************服务时间设置*************/
            deviceService.setServiceTime(device);
            /*************完成服务时间设置*************/

            //device得到机型
            Model model=device.getModel();
            //model得到解析表集合
            List<Parse> parses= parseDAO.findByModel(model);
            /***********对传输内容的长度和parse表的长度进行校验**********/
            int maxLength= parses.get(parses.size()-1).getByteEnd(); //parse中最长的字节数
            int dataLenth= (intData[16]*256+intData[17])/8;  //信息-电文长度
            if(maxLength !=dataLenth){
                //长度不匹配则出错，退出解析
                return Result.error("长度不匹配则出错，退出解析");
            }

            //开始遍历parse表解析数据
            List<BigDecimal> dataValues =new ArrayList<BigDecimal>();  //数据解析结果的集合
            List<String> infoString= new ArrayList<>();
            List<Alarm> alarms =new ArrayList<>();      //解析报警信息的集合
            for (Parse parse: parses){
                BigDecimal dataValue=ParseUtil.getValueByParse(intData,parse);//解析数据
                /*************开始报警检查*************/
                Alarm alarm =alarmService.judgeAlarm(parse,dataValue,device);
                //单个数据生成的alarm不为空，即数据不正常,则放入集合中
                if( alarm.getAlarmType() != 2){ //2是经过检查无异常
                    alarms.add(alarm);
                }
                /*************完成报警检查*************/
                dataValues.add(dataValue);//数值加入集合
                //判断是否需要调用匹配文字信息函数(matchService.getStringByMatch会自己根据parse判断)
                String str=matchService.getStringByMatch(parse,dataValue);
                if(! str.isEmpty())
                    infoString.add(str);
            }
            /*************开始储存数据到data实体中*************/

            dataDeviceService.addData(dataValues, infoString,device);

            /*************完成储存*************/

            /********对得到的Alarm集合进行处理*********/
            //alarm集合为空，表示数据正常
            if (alarms.isEmpty()){
                if (device.getStatus() == 1){
                    //设备状态本身为正常，不做任何处理
                    System.out.println("alarm集合为空，数据正常");
                    return Result.ok("alarm集合为空，数据正常");
                }
                else if (device.getStatus() == 2){
                    //设备状态本身为报警,即报警状态的设备接收到了正常的数据，需要对ALarm写入endTime
                    alarmService.setEndTime(device);
                    System.out.println("报警设备得到正常数据，恢复正常");
                    device.setStatus(1);
                    deviceDAO.save(device);
                    return Result.ok("报警设备得到正常数据，恢复正常");
                }
            }
            //数据不正常，需报警,报警前先看看设备的状态
            else {
                if (device.getStatus() == 2){
                    //设备状态本身为报警，不做任何处理
                }
                else if (device.getStatus() == 1){
                    //设备状态本身为正常，将设备状态置为报警，报警信息存储到数据库
                    device.setStatus(2);
                    deviceDAO.save(device);
                    for (Alarm alarm: alarms)
                        alarmService.add(alarm);
                }
            }
            return Result.ok();
    }
}
